﻿using System;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Collections.Generic;

namespace Streambolics.Gui
{
    public class NoSystemMenuException : System.Exception
    {
    }

    // Values taken from MSDN.
    public enum ItemFlags
    { // The item ...
        mfUnchecked = 0x00000000,    // ... is not checked
        mfString = 0x00000000,    // ... contains a string as label
        mfDisabled = 0x00000002,    // ... is disabled
        mfGrayed = 0x00000001,    // ... is grayed
        mfChecked = 0x00000008,    // ... is checked
        mfPopup = 0x00000010,    // ... Is a popup menu. Pass the
        //     menu handle of the popup
        //     menu into the ID parameter.
        mfBarBreak = 0x00000020,    // ... is a bar break
        mfBreak = 0x00000040,    // ... is a break
        mfByPosition = 0x00000400,    // ... is identified by the position
        mfByCommand = 0x00000000,    // ... is identified by its ID
        mfSeparator = 0x00000800     // ... is a seperator (String and
        //     ID parameters are ignored).
    }

    
    public class SystemMenuItem
    {
        public event EventHandler Click;

        internal void DoClick ()
        {
            OnClick (EventArgs.Empty);
        }

        protected virtual void OnClick (EventArgs e)
        {
            if (Click != null)
            {
                Click (this, e);
            }
        }
    }

    /// <summary>
    /// A class that helps to manipulate the system menu
    /// of a passed form.
    /// 
    /// Based on code originally written by Florian "nohero" Stinglmayr
    /// </summary>

    public class SystemMenu
    {
        [DllImport("USER32", EntryPoint = "GetSystemMenu", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
        private static extern IntPtr apiGetSystemMenu(IntPtr WindowHandle, int bReset);

        [DllImport("USER32", EntryPoint = "AppendMenuW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
        private static extern int apiAppendMenu(IntPtr MenuHandle, int Flags, int NewID, String Item);

        [DllImport("USER32", EntryPoint = "InsertMenuW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
        private static extern int apiInsertMenu(IntPtr hMenu, int Position, int Flags, int NewId, String Item);

        private IntPtr _MenuHandle = IntPtr.Zero;
        private int _NextId = 0x100;
        private Dictionary<int, SystemMenuItem> _Handlers = new Dictionary<int, SystemMenuItem> ();

        public SystemMenu(IntPtr aHandle)
        {
            if (aHandle == IntPtr.Zero)
            {
                throw new NoSystemMenuException();
            }
            _MenuHandle = aHandle;
        }

        public SystemMenu (Form aForm)
            : this (apiGetSystemMenu(aForm.Handle, 0))
        {
        }

        public bool Intercept(ref Message aMsg)
        {
            if (aMsg.Msg == 0x0112)
            {
                int id = (int)(aMsg.WParam);
                if (_Handlers.ContainsKey (id))
                {
                    _Handlers[id].DoClick();
                    return true;
                }
            }
            return false;
        }

        public SystemMenuItem Append (string aText)
        {
            int n = _NextId++;
            SystemMenuItem m = new SystemMenuItem();
            _Handlers.Add(n, m);
            AppendMenu (n, aText);
            return m;      
        }

        // Insert a separator at the given position index starting at zero.
        public bool InsertSeparator(int Pos)
        {
            return (InsertMenu(Pos, ItemFlags.mfSeparator | ItemFlags.mfByPosition, 0, ""));
        }

        // Simplified InsertMenu(), that assumes that Pos is a relative
        // position index starting at zero
        public bool InsertMenu(int Pos, int ID, String Item)
        {
            return (InsertMenu(Pos, ItemFlags.mfByPosition |
                                ItemFlags.mfString, ID, Item));
        }

        // Insert a menu at the given position. The value of the position
        // depends on the value of Flags. See the article for a detailed
        // description.
        public bool InsertMenu(int Pos, ItemFlags Flags, int ID, String Item)
        {
            return (apiInsertMenu(_MenuHandle, Pos, (Int32)Flags, ID, Item) == 0);
        }

        // Appends a seperator
        public bool AppendSeparator()
        {
            return AppendMenu(0, "", ItemFlags.mfSeparator);
        }

        // This uses the ItemFlags.mfString as default value
        public bool AppendMenu(int ID, String Item)
        {
            return AppendMenu(ID, Item, ItemFlags.mfString);
        }
        // Superseded function.
        public bool AppendMenu(int ID, String Item, ItemFlags Flags)
        {
            return (apiAppendMenu(_MenuHandle, (int)Flags, ID, Item) == 0);
        }

        // Reset's the window menu to it's default
        public static void ResetSystemMenu(Form aForm)
        {
            apiGetSystemMenu(aForm.Handle, 1);
        }

        // Checks if an ID for a new system menu item is OK or not
        public static bool VerifyItemID(int ID)
        {
            return (bool)(ID < 0xF000 && ID > 0);
        }
    }
}
